#include "Int_NorFlash.h"

// 静态函数：发送打开写使能的命令
static void Int_NorFlash_WriteEnable(void)
{
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送写使能命令
    Dri_SPI_SwapByte(0x06);

    // 结束通信
    Dri_SPI_Stop();
}

// 静态函数：发送关闭写使能的命令
static void Int_NorFlash_WriteDisable(void)
{
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送写禁用命令
    Dri_SPI_SwapByte(0x04);

    // 结束通信
    Dri_SPI_Stop();
}

// 静态函数：等待NorFlash不忙
static void Int_NorFlash_WaitNotBusy(void)
{
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送读取 Read Status Register-1 命令
    Dri_SPI_SwapByte(0x05);

    // 如果 NorFlash 再忙，就一直等
    uint8_t status;
    do
    {
        // 读取状态状态值
        status = Dri_SPI_SwapByte(0x00);
    } while (status & 0x01);

    // 结束通信
    Dri_SPI_Stop();
}

// 静态函数：擦除NorFlash的一个扇区
// @param block_addr 块地址, 范围 0 ~ 63
// @param sector_addr 扇区地址, 范围 0 ~ 15
static void Int_NorFlash_EraseSector(uint8_t block_addr, uint8_t sector_addr)
{
    // 等待 NorFalsh 不忙
    Int_NorFlash_WaitNotBusy();

    // 打开写使能
    Int_NorFlash_WriteEnable();

    /************ 擦除操作 start ***************/
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送扇区擦除命令
    Dri_SPI_SwapByte(0x20);

    // 向 NorFash 发送块地址和扇区地址
    Dri_SPI_SwapByte(block_addr);       // 发送块地址
    Dri_SPI_SwapByte(sector_addr << 4); // 发送扇区地址+页地址
    Dri_SPI_SwapByte(0x00);             // 发送字节地址

    // 结束通信
    Dri_SPI_Stop();

    /************* 擦除操作 end **************/

    // 关闭写使能
    Int_NorFlash_WriteDisable();
}

/* ***************************************************************************************** */
/* ***************************************************************************************** */
/**
 * @brief NorFlash初始化
 *
 */
void Int_NorFlash_Init(void)
{
    // 初始化 SPI
    Dri_SPI_Init();
}

/**
 * @brief 获取NorFlash的Manufacture ID和Product ID
 *
 * @param mid 用于存储Manufacture ID的指针
 * @param pid 用于存储Product ID的指针
 */
void Int_NorFlash_GetProductID(uint8_t *mid, uint16_t *pid)
{
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送 JEDEC ID 命令
    Dri_SPI_SwapByte(0x9F);

    // 读取 Manufacturer ID
    *mid = Dri_SPI_SwapByte(0x00);

    // 读取 Product ID 的高字节
    *pid = Dri_SPI_SwapByte(0x00);
    // 读取 Product ID 的低字节
    *pid <<= 8;
    *pid |= Dri_SPI_SwapByte(0x00);

    // 结束通信
    Dri_SPI_Stop();
}

/**
 * @brief 向NorFlash中指定的页写入指定长度的数据
 *
 * @param block_addr    块地址
 * @param sector_addr   扇区地址
 * @param page_addr     页地址
 * @param data          要写入的数据指针
 * @param len           要写入的数据长度，范围为1到256
 */
void Int_NorFlash_WritePage(uint8_t block_addr, uint8_t sector_addr, uint8_t page_addr, uint8_t *data, uint16_t len)
{
    // 擦除该页所在扇区
    Int_NorFlash_EraseSector(block_addr, sector_addr);

    // 等待 NorFalsh 不忙
    Int_NorFlash_WaitNotBusy();

    // 打开写使能
    Int_NorFlash_WriteEnable();

    /************ 写入操作 start ***************/
    // 开始通信
    Dri_SPI_Start();

    // 向 NorFash 发送页编程命令
    Dri_SPI_SwapByte(0x02);

    // 向 NorFash 发送块地址、扇区地址、页地址
    Dri_SPI_SwapByte(block_addr);                     // 发送块地址
    Dri_SPI_SwapByte((sector_addr << 4) | page_addr); // 发送扇区地址+页地址
    Dri_SPI_SwapByte(0x00);                           // 发送字节地址

    // 向 NorFash 发送数据
    for (uint16_t i = 0; i < len; i++)
    {
        Dri_SPI_SwapByte(data[i]);
    }

    // 结束通信
    Dri_SPI_Stop();

    /************ 写入操作 end *****************/

    // 关闭写使能
    Int_NorFlash_WriteDisable();
}

/**
 * @brief 读取NorFlash中指定位置指定长度的数据
 *
 * @param block_addr    块地址
 * @param sector_addr   扇区地址
 * @param page_addr     页地址
 * @param byte_addr     字节地址
 * @param data          用于存储读取数据的指针
 * @param len           要读取的数据长度，范围为1到256
 */
void Int_NorFlash_ReadData(uint8_t block_addr, uint8_t sector_addr, uint8_t page_addr, uint8_t byte_addr, uint8_t *data, uint16_t len)
{
    // 等待NorFlash不忙
    Int_NorFlash_WaitNotBusy();

    // 开始通信
    Dri_SPI_Start();

    // 向NorFlash发送读取命令
    Dri_SPI_SwapByte(0x03);

    // 向NorFlash发送块地址、扇区地址、页地址、字节地址
    Dri_SPI_SwapByte(block_addr);                     // 块地址
    Dri_SPI_SwapByte((sector_addr << 4) | page_addr); // 扇区地址+页地址
    Dri_SPI_SwapByte(byte_addr);                      // 字节地址

    // 读取数据
    for (uint16_t i = 0; i < len; i++)
    {
        data[i] = Dri_SPI_SwapByte(0x00);
    }

    // 结束通信
    Dri_SPI_Stop();
}
